import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
import java.util.Random;

import edu.rit.ds.RemoteEventListener;
import edu.rit.ds.registry.NotBoundException;
import edu.rit.ds.registry.RegistryEvent;
import edu.rit.ds.registry.RegistryEventFilter;
import edu.rit.ds.registry.RegistryEventListener;
import edu.rit.ds.registry.RegistryProxy;

/**
 * Master.java
 * This class creates a matrix and sends it to Slave for matrix multiplication
 * @author Distributed Warriors
 *
 */
public class Master{
	//Host address of registry server
	private static String host;
	//Port number of 
	private static int port;
	private static int nodes;
	private static RegistryProxy registry;
	private static RegistryEventListener reglistener;
	private static RegistryEventFilter regfilter;
	private static RemoteEventListener<SlaveEvent> listener;
	private static int size;				//size of matrix...... Assumption: Matrix is square
	private static int[][] matrix;
	private static int[][] result;
	private static int count = 0;
	private static long startTime;
	private static long endTime;
	
	public Master(String[] args){
		if(args.length!=4){
			throw new IllegalArgumentException("Usage: java Master host port size nodes");
		}
		host = args[0];
		try{
			port  =Integer.parseInt(args[1]);
		}catch (NumberFormatException e){
			throw new IllegalArgumentException("Port number not valid");
		}
		try{
			size = Integer.parseInt(args[2]);
		}catch (NumberFormatException e){
			throw new IllegalArgumentException("Size not valid");
		}
		try{
			nodes = Integer.parseInt(args[3]);
		}catch (NumberFormatException e){
			throw new IllegalArgumentException("Size not valid");
		}
		try {
			registry = new RegistryProxy(host,port);
		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		reglistener = new RegistryEventListener(){
     		public void report (long seqnum, RegistryEvent event){
        		listenServer (event.objectName());
        	}
		};
  		try {
			UnicastRemoteObject.exportObject (reglistener, 0);
		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
  		listener = new RemoteEventListener<SlaveEvent>(){
			public synchronized void report (long seqnum, SlaveEvent event){
				count++;
				for(int i=0;i<event.elements.length;i++){
					result[i][event.colId] = event.elements[i];
				}
				if(count==size){
					endTime = System.currentTimeMillis();
					System.out.println("Time taken by "+nodes+" slaves to multiply "+size+" x "+size+" is "+(endTime-startTime)+" milliseconds");
					return;
				}
			}
		};
		try {
			UnicastRemoteObject.exportObject (listener, 0);
		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		regfilter = new RegistryEventFilter()
         	.reportType ("SlaveInt")
         	.reportBound();
		try {
			registry.addEventListener (reglistener, regfilter);
		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			for (String object : registry.list ("SlaveInt")){
			 	listenServer (object);
			}
		} catch (RemoteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		generateMatrix();
		try{
			List<String> slaves = registry.list("SlaveInt");
			for(int i = 0;i<nodes;i++){
				SlaveInt slave = (SlaveInt)registry.lookup(slaves.get(i));
				slave.sendMatrix(matrix, matrix, size);
			}
			int colId = -1;
			startTime = System.currentTimeMillis();
			do{
				for(int i = 0;i<nodes;i++){
					SlaveInt slave = (SlaveInt)registry.lookup(slaves.get(i));
					slave.calculate(++colId);
					if(colId==size-1){
						break;
					}
				}
			}while(colId<size-1);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	private static void listenServer(String object){
		try{
			SlaveInt server = (SlaveInt)registry.lookup(object);
			server.addListener (listener);
		}
 		catch(NotBoundException exc){
 			exc.printStackTrace();
        }
      	catch (RemoteException exc){
      		exc.printStackTrace();
        }
		catch (Exception e){
			e.printStackTrace();
		}
	}

	public static void generateMatrix(){
		matrix = new int[size][size];
		result = new int[size][size];
		Random gen  = new Random();
		gen.setSeed(23454567L);
		for(int i = 0;i<size;i++){
			for(int j=0;j<size;j++){
				matrix[i][j] = gen.nextInt(10);
			}
		}
	}
}
